package com.bianmaba.spring.security.database.access.intercept;

import com.bianmaba.spring.security.basic.configuration.SecurityPropertiesConfiguration;
import com.bianmaba.spring.security.database.access.DatabaseAccessDecisionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.transaction.Transactional;
import java.util.*;

import static com.bianmaba.spring.security.autoconfig.WebSecurityAutoConfiguration.ORDER;


@Service
@Transactional
@ConditionalOnClass({WebSecurityConfigurerAdapter.class})
//如果此类加载前已经存在实例，则此类将不会被实例化
@ConditionalOnMissingBean({DatabaseSecurityMetadataSource.class})
@Order(ORDER)//配置初始化排序，此值越低，越靠前，但@Import引入的类此注解无效
public class DatabaseSecurityMetadataSource implements FilterInvocationSecurityMetadataSource, InitializingBean {
    protected static final Logger LOG = LoggerFactory.getLogger(DatabaseAccessDecisionManager.class);

    public DatabaseSecurityMetadataSource() {
        if (this.getClass().getName().equals(DatabaseSecurityMetadataSource.class.getName())) ;
        {
            LOG.info("加载安全性安全元数据源(order=" + ORDER + "):" + this.getClass().getName());
        }
    }

    @Autowired
    private SecurityPropertiesConfiguration.WebSecurityProperties securityProperties;

    @Autowired
    private ResourceDetailsService resourceService;


    protected final Map<RequestMatcher, Collection<ConfigAttribute>> requestMapping = new HashMap<RequestMatcher, Collection<ConfigAttribute>>(0);

    protected List<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>();

    protected Long lastUpdateCache = null;

    public boolean supports(Class<?> clazz) {
        return true;
    }

    /**
     * 从数据库中查询所有有效资源
     *
     * @return
     */
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        LOG.debug("获取资源列表:getAllConfigAttributes()");
        //从配置文件中读取所有资源
        List<String> resources = getResourcesFromConfigurationProperties();
        if (resources == null) {
            resources = new ArrayList<>(0);
        }
        //从ResourceDetailsService或其实现类中读取所有资源
        List<String> serviceResources = resourceService.getAllResources();
        if (serviceResources != null) {
            resources.addAll(serviceResources);
        }

        for (String resource : resources) {
            if (resource != null && !resource.trim().isEmpty()) {
                configAttributes.add(new SecurityConfig(resource));
                AntPathRequestMatcher matcher = new AntPathRequestMatcher(resource);

                //从配置文件中读取指定资源对应的许可
                List<String> authorities = findAuthoritiesConfigurationProperties(resource);
                if (authorities == null) {
                    authorities = new ArrayList<>(0);
                }
                authorities.addAll(resourceService.findAuthorities(resource));
                //从ResourceDetailsService或其实现类中读取指定资源对应的许可
                List<String> serviceAuthorities = resourceService.getAllResources();
                if (serviceResources != null) {
                    serviceResources.addAll(serviceAuthorities);
                }

                List<ConfigAttribute> list = new ArrayList<ConfigAttribute>(0);

                for (String authority : authorities) {
                    if (!authority.startsWith("ROLE_")) {
                        list.add(new SecurityConfig("ROLE_" + authority));
                    } else {
                        list.add(new SecurityConfig("ROLE_" + authority));
                    }
                }
                requestMapping.put(matcher, list);
            }
        }
        return configAttributes;
    }

    // 返回所请求资源所需要的权限(对应系统中的角色)
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object)
            throws IllegalArgumentException {
        HttpServletRequest request = ((FilterInvocation) object).getRequest();
        if (lastUpdateCache == null || ((System.currentTimeMillis() - lastUpdateCache) / 1000 > securityProperties.getResourceCacheTime())) {
            configAttributes.clear();
            requestMapping.clear();
            getAllConfigAttributes();
            lastUpdateCache = System.currentTimeMillis();
        }
        List<ConfigAttribute> attributes = new ArrayList<>(0);
        Set<Map.Entry<RequestMatcher, Collection<ConfigAttribute>>> entries = this.requestMapping.entrySet();
        for (Map.Entry<RequestMatcher, Collection<ConfigAttribute>> entry : entries) {
            if (entry.getKey().matches(request)) {
                attributes.addAll(entry.getValue());
            }
        }
        return attributes;
    }

    private List<String> getResourcesFromConfigurationProperties() {
        List<String> list = new ArrayList<String>(0);
        List<Map<String, String>> results = securityProperties.getResources();
        for (Map<String, String> result : results) {
            list.add(result.get("path"));
        }
        return list;
    }

    private List<String> findAuthoritiesConfigurationProperties(String resource) {
        List<String> list = new ArrayList<String>(0);
        List<Map<String, String>> results = securityProperties.getResources();
        for (Map<String, String> result : results) {
            if (result.get("path").equals(resource)) {
                String rolesStr = result.get("roles");
                if (rolesStr != null) {
                    String[] roles = rolesStr.trim().split("[,]");
                    for (String role : roles) {
                        if (!role.trim().isEmpty()) {
                            list.add(role);
                        }
                    }
                }
                break;
            }
        }
        return list;
    }

    @Override
    public void afterPropertiesSet() throws Exception {

    }


    @Configuration
    //如果此类加载前已经存在实例，则此类将不会被实例化
    @ConditionalOnMissingBean({ResourceDetailsService.class})
    @Order(ORDER - 1)//配置初始化排序，此值越低，越靠前，但@Import引入的类此注解无效
    public static class ResourceDetailsService {
        protected static final Logger LOG = LoggerFactory.getLogger(ResourceDetailsService.class);

        public ResourceDetailsService() {
            if (this.getClass().getName().equals(ResourceDetailsService.class.getName())) ;
            {
                LOG.info("加载资源服务(order=" + ORDER + "):" + this.getClass().getName());
            }
        }

        public List<String> getAllResources() {
            return new ArrayList<String>(0);
        }

        public List<String> findAuthorities(String resource) {
            return new ArrayList<String>(0);
        }

    }
}
